package com.googlecode.horncomb.web.spring.mvc;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.multipart.support.ByteArrayMultipartFileEditor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.view.RedirectView;

import com.googlecode.horncomb.util.lang.DateUtils;
import com.googlecode.horncomb.util.lang.GenericsUtils;

/**
 * 扩展Sping MVC的SimpleFormController
 * 
 * @author Daniel
 */
public class BaseSimpleFormController<FormType extends Form> extends
		SimpleFormController {
	protected String defaultFormEvent = "show"; //$NON-NLS-1$
	protected String urlEncoding = "UTF-8"; //$NON-NLS-1$
	/**
	 * 日期格式
	 */
	protected String dateFormate = DateUtils.FORMAT_DATE;

	/**
	 * 成功重定向视图
	 */
	private String successRedirectView = null;

	/**
	 * 默认表单事件字段名
	 */
	protected String formEventFieldName = "event"; //$NON-NLS-1$

	public BaseSimpleFormController() {
		super();
		// 新表单（get）时候也进行构造
		this.setBindOnNewForm(true);
		// set command name
	}

	@Override
	protected void initApplicationContext() {
		super.initApplicationContext();
		if (this.getCommandClass() == null
				&& StringUtils.isEmpty(this.getCommandName())) {
			Class<?> type = GenericsUtils.getFirstGenericClass(this.getClass());
			if (type != null) {
				this.setCommandName(StringUtils.uncapitalize(type
						.getSimpleName()));
				this.setCommandClass(type);
			}
		} else if (this.getCommandClass() != null) {
			this.setCommandName(StringUtils.uncapitalize(this.getCommandClass()
					.getSimpleName()));
		}
		if (this.getCommandClass() == null) {
			throw new IllegalStateException(
					"Cannot create command without commandClass being set - "
							+ "either set commandClass or (in a form controller) override formBackingObject");
		}
	}

	protected void onBindOnNewForm(HttpServletRequest request, Object command)
			throws Exception {
		this.convertCommandType(command).setEvent(this.defaultFormEvent);
	}

	/**
	 * 把Object转换成表单对象。
	 * 
	 * @param command
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public FormType convertCommandType(Object command) {
		return (FormType) command;
	}

	/**
	 * 初始化绑定日期、数值编辑器
	 * 
	 * @see org.springframework.web.servlet.mvc.BaseCommandController#initBinder(javax.servlet.http.HttpServletRequest,
	 *      org.springframework.web.bind.ServletRequestDataBinder)
	 */

	protected void initBinder(HttpServletRequest request,
			ServletRequestDataBinder binder) throws Exception {
		// 允许为空的绑定
		// bind DateEditor
		SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormate);
		binder.registerCustomEditor(Date.class, new CustomDateEditor(
				dateFormat, true));
		// bind IntegerEditor
		binder.registerCustomEditor(Integer.class, new CustomNumberEditor(
				Integer.class, true));
		// bind LongEditor
		binder.registerCustomEditor(Long.class, new CustomNumberEditor(
				Long.class, true));
		// bind DoubleEditor
		binder.registerCustomEditor(Double.class, new CustomNumberEditor(
				Double.class, true));
		// bind ByteArrayMultipartFileEditor for file upload
		binder.registerCustomEditor(byte[].class,
				new ByteArrayMultipartFileEditor());
	}

	/**
	 * 默认的表单需要改变模型，还是单纯的视图模型获取。 判断条件是，原始的表单事件是否改变。
	 * 
	 * @see org.springframework.web.servlet.mvc.SimpleFormController#isFormChangeRequest(javax.servlet.http.HttpServletRequest)
	 */
	protected boolean isFormChangeRequest(HttpServletRequest request) {
		String event = request.getParameter(this.formEventFieldName);
		return StringUtils.equals(event, this.defaultFormEvent);
	}

	/**
	 * 默认的提交操作的模型数据引用
	 * 
	 * @param request
	 * @param command
	 * @param errors
	 * @return
	 * @throws Exception
	 */
	protected Map<Object, Object> submitReference(HttpServletRequest request,
			FormType command, Errors errors) throws Exception {
		return submitReferenceData(request);
	}

	protected Map<Object, Object> submitReferenceData(HttpServletRequest request)
			throws Exception {
		return null;
	}

	/**
	 * 默认提交转向到视图。 原则上优先服从successView，然后是redirectView，最后是formView
	 * 
	 * @see org.springframework.web.servlet.mvc.SimpleFormController#onSubmit(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse, java.lang.Object,
	 *      org.springframework.validation.BindException)
	 */
	protected ModelAndView onSubmit(HttpServletRequest request,
			HttpServletResponse response, Object command, BindException errors)
			throws Exception {
		FormType form = this.convertCommandType(command);
		ModelAndView mv = onSubmit(form);
		if (mv != null) {
			// simplest onSubmit variant implemented in custom subclass
			return mv;
		} else {
			// default behavior: render success view
			if (getSuccessView() == null) {
				throw new ServletException("successView isn't set");
			}
			return this.resolveResultView(request, response, form);
		}
	}

	/**
	 * 提交
	 * 
	 * @param request
	 * @param command
	 * @param errors
	 * @return
	 * @throws Exception
	 */
	protected void doSubmitAction(FormType command) throws Exception {
	}

	protected void onFormChange(HttpServletRequest request,
			HttpServletResponse response, Object command, BindException errors)
			throws Exception {
		FormType domain = this.convertCommandType(command);
		onFormChange(request, response, domain, errors);
	}

	protected void onFormChange(HttpServletRequest request,
			HttpServletResponse response, FormType formObj, BindException errors)
			throws Exception {
	}

	/**
	 * 解释产生返回的视图
	 * 
	 * @param request
	 * @param response
	 * @param form
	 * @return
	 * @throws Exception
	 */
	protected ModelAndView resolveResultView(HttpServletRequest request,
			HttpServletResponse response, FormType form) throws Exception {
		if (getSuccessView() != null && getSuccessView().equals(getFormView())) {
			setSuccessView(null);
		}
		// 原则上优先服从successView，然后是redirectView，然后是formView
		if (getSuccessView() != null) {
			return new ModelAndView(this.getSuccessView(), this
					.getSuccessViewReference(request, response, form));
		} else if (getSuccessRedirectView() != null) {
			String path = getSuccessRedirectView();
			RedirectView rv = new RedirectView(path, true);
			return new ModelAndView(rv, this.getSuccessRedirectViewReference(
					request, response, form));
		} else if (getFormView() != null) {
			request.setAttribute(this.getCommandName(), form);
			return new ModelAndView(this.getFormView(), this.referenceData(
					request, form, null));
		} else {
			throw new ServletException(
					"formView, successView or successRedirectView aren't set");
		}
	}

	protected Map<String, Object> getSuccessViewReference(
			HttpServletRequest request, HttpServletResponse response,
			FormType command) throws Exception {
		return null;
	}

	protected Map<String, Object> getSuccessRedirectViewReference(
			HttpServletRequest request, HttpServletResponse response,
			FormType command) throws Exception {
		return null;
	}

	public String getSuccessRedirectView() {
		return successRedirectView;
	}

	public void setSuccessRedirectView(String successRedirectView) {
		this.successRedirectView = successRedirectView;
	}

	public void setFormEventFieldName(String eventFieldName) {
		this.formEventFieldName = eventFieldName;
	}

	public void setDateFormate(String dateFormate) {
		this.dateFormate = dateFormate;
	}

	/**
	 * @param urlEncoding
	 *            the urlEncoding to set
	 */
	public void setUrlEncoding(String urlEncoding) {
		this.urlEncoding = urlEncoding;
	}

	/**
	 * @param defaultFormEvent
	 *            the defaultFormEvent to set
	 */
	public void setDefaultFormEvent(String defaultFormEvent) {
		this.defaultFormEvent = defaultFormEvent;
	}
}
